require "lunit"
require "argot"

lunit.setprivfenv()
lunit.import "assertions"

-- little helper
function makeBoundFunction(obj, method)
	func = function(...)
	            -- table.foreach(arg, print)
				return method(obj, unpack(arg))
			end
	return func
end


-- a little class which we can use for binding stuff

CTypeElementTest = {}
CTypeElementTest_mt = { __index = CTypeElementTest }

-- This function creates a new instance of TypeElementTest
function CTypeElementTest.create(self)
    local new_inst = { element = argot.TypeElement(makeBoundFunction(self, self.bind)) }    -- the new instance
    setmetatable( new_inst, CTypeElementTest_mt ) -- all instances share the same metatable
    return new_inst
end

function CTypeElementTest.bind(self, element, definition)
	print('I was called! Hooray!')

    rc = element.object:bind( definition.library, argot.meta_basic_name )
	if rc ~= argot.TYPE_OK then return rc end

    element:Bind( definition )
    rc = element.object:bind( definition.library, argot.meta_basic_name )
    if rc ~= argot.TYPE_OK then return rc end

    print('Exiting bind callback')

    return argot.TYPE_OK

end

function CTypeElementTest.read(streamin, element)
	print('read')
	return 0
end

function CTypeElementTest.write(streamout, data, element)
	print('write')
	return 0
end

TestTypeElement = makeBoundFunction( CTypeElementTest, CTypeElementTest.create )


assert_raises = assert_error


-- real test case goes here

LibraryTest = lunit.TestCase("TypeLibrary")


function LibraryTest.setup(self)
    self.lib = argot.TypeLibrary()
	assert( argot.type_core_init( self.lib ) == argot.TYPE_OK )
 end

function LibraryTest.testReserveType(self)
    name = argot.TypeName( 'test' )
    id = self.lib:reserve( name )
    assert( id >= 0 )
    assert( self.lib:get_state( name ) == argot.TYPE_RESERVED )
end

function LibraryTest.testNotDefinedTypeState(self)
    name = argot.TypeName( 'blah' )
    assert( self.lib:get_state( name ) == argot.TYPE_NOT_DEFINED )
end

function LibraryTest.testRegisterStructure(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    id = self.lib:Register( test, element.element )
    assert( id >= 0 )
    assert( self.lib:get_state( test ) == argot.TYPE_REGISTERED )
end

function LibraryTest.testRegisterComplete(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    id = self.lib:Register_bind( test, element.element, CTypeElementTest.read, CTypeElementTest.write )
    assert( id >= 0 )
    assert( self.lib:get_state( test ) == argot.TYPE_COMPLETE )
end

function LibraryTest.testRegisterAfterReserve(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    self.lib:reserve( test )
    id = self.lib:Register_bind( test, element.element, CTypeElementTest.read, CTypeElementTest.write )
    assert( id >= 0 )
    assert( self.lib:get_state( test ) == argot.TYPE_COMPLETE )

    structure = self.lib:get_structure( self.lib:get_id( test ) )
    assert( structure ~= nil )
end

function LibraryTest.testBind(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    id = self.lib:Register( test, element.element )
    assert( id >= 0 )
    self.lib:Bind( test, CTypeElementTest.read, CTypeElementTest.write )
end

function LibraryTest.testBindException(self)
    test = argot.TypeName( 'test' )

    self.lib:Bind( test, CTypeElementTest.read, CTypeElementTest.write )
end

function LibraryTest.testGetStructure(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    id = self.lib:Register_bind( test, element.element, CTypeElementTest.read, CTypeElementTest.write )
    elem = self.lib:get_structure( self.lib:get_id( test ) )
    assert( element.element.this == elem.this )
end

function LibraryTest.testGetReader(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()
    reader = CTypeElementTest.read
    self.lib:Register_bind( test, element.element, reader, CTypeElementTest.write )

    read = self.lib:get_reader( self.lib:get_id( test ) )

    assert( read == reader )
end

function LibraryTest.testGetReaderFail(self)
    test = argot.TypeName( 'test' )
    badtype = argot.TypeName( 'badtype' )
    element = TestTypeElement()

    self.lib:Register( test, element.element )
    assert_raises( function () self.lib:get_reader( self.lib:get_id( test ) ) end )

    assert_raises( function () self.lib:get_id( badtype ) end )
    assert_raises( function () self.lib:get_reader( -99 ) end )
end

function LibraryTest.testGetWriter(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    writer = CTypeElementTest.write
    self.lib:Register_bind( test, element.element, CTypeElementTest.read, writer )

    write = self.lib:get_writer( self.lib:get_id( test ) )

    assert( writer == write )
end

function LibraryTest.testGetWriterFail(self)
    test = argot.TypeName( 'test' )
    badtype = argot.TypeName( 'badtype' )
    element = TestTypeElement()

    self.lib:Register( test, element.element )
    assert_raises( function () self.lib:get_writer( self.lib:get_id( test ) ) end )

    assert_raises( function () self.lib:get_id( badtype ) end )
    assert_raises( function () self.lib:get_writer( -99 ) end )
end

function LibraryTest.testRegisterAfterReserveCheckId(self)
    test = argot.TypeName( 'test' )
    element = TestTypeElement()

    idres = self.lib:reserve( test )
    idreg = self.lib:Register( test, element.element )
    assert( idres == idreg )
end

function LibraryTest.testNullNameForGetId(self)
    assert_raises( function () self.lib:get_id(nil) end )
end                                                 

function LibraryTest.testSomeExceptions(self)
    testtype = argot.TypeName( 'test' )
    emptytype = argot.TypeName( '' )
    element = TestTypeElement()

    assert_raises( function () self.lib:Register( testtype, nil ) end )
    assert_raises( function () self.lib:Register( emptytype, nil ) end )
    assert_raises( function () self.lib:Register( nil, element.element ) end )

    self.lib:Register( testtype, element.element )
    assert_raises( function () self.lib:Register( testtype, element.element ) end )
end


lunit.run()